package cn.sciento.transfer.infra.datasource.properties;

import com.fasterxml.jackson.databind.ObjectMapper;
import cn.sciento.core.exception.CommonException;import java.io.IOException;
import java.util.Map;
import java.util.Optional;
import javax.sql.DataSource;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.context.properties.bind.Bindable;
import org.springframework.boot.context.properties.bind.Binder;
import org.springframework.boot.context.properties.source.ConfigurationPropertyName;
import org.springframework.boot.context.properties.source.ConfigurationPropertySource;
import org.springframework.boot.context.properties.source.MapConfigurationPropertySource;
import org.springframework.util.Assert;
import org.springframework.util.ClassUtils;

public class BasicDataSourceProperties {
  private static Logger LOGGER = LoggerFactory.getLogger(BasicDataSourceProperties.class);
  
  private DataSourcePoolType dataSourcePoolType;
  
  private String driverClassName;
  
  private String url;
  
  private String username;
  
  private String password;
  
  private String options;
  
  public DataSourcePoolType getDataSourcePoolType() {
    return this.dataSourcePoolType;
  }
  
  public BasicDataSourceProperties setDataSourcePoolType(DataSourcePoolType dataSourcePoolType) {
    this.dataSourcePoolType = dataSourcePoolType;
    return this;
  }
  
  public String getDriverClassName() {
    return this.driverClassName;
  }
  
  public BasicDataSourceProperties setDriverClassName(String driverClassName) {
    this.driverClassName = driverClassName;
    return this;
  }
  
  public String getUrl() {
    return this.url;
  }
  
  public BasicDataSourceProperties setUrl(String url) {
    this.url = url;
    return this;
  }
  
  public String getUsername() {
    return this.username;
  }
  
  public BasicDataSourceProperties setUsername(String username) {
    this.username = username;
    return this;
  }
  
  public String getPassword() {
    return this.password;
  }
  
  public BasicDataSourceProperties setPassword(String password) {
    this.password = password;
    return this;
  }
  
  public String getOptions() {
    return this.options;
  }
  
  public BasicDataSourceProperties setOptions(String options) {
    this.options = options;
    return this;
  }
  
  public void validate() {
    Assert.notNull(this.url, "data source url cannot be null.");
    Assert.notNull(this.username, "data source user name cannot be null.");
    Assert.notNull(this.password, "data source password cannot be null.");
  }
  
  public Class<? extends DataSource> buildType() {
    try {
      String poolClass = ((DataSourcePoolType)Optional.<DataSourcePoolType>ofNullable(this.dataSourcePoolType).orElse(DataSourcePoolType.DEFAULT)).dataSourcePoolClass();
      Class<? extends DataSource> type = (Class<? extends DataSource>) ClassUtils.forName(poolClass, BasicDataSourceProperties.class.getClassLoader());
      return type;
    } catch (ClassNotFoundException ex) {
      throw new CommonException(ex);
    } 
  }
  
  public void bindProperties(Object target) {
    if (StringUtils.isNotEmpty(this.options)) {
      ObjectMapper objectMapper = new ObjectMapper();
      try {
        Map properties = (Map)objectMapper.readValue(this.options, Map.class);
        MapConfigurationPropertySource mapConfigurationPropertySource = new MapConfigurationPropertySource(properties);
        Binder binder = new Binder(new ConfigurationPropertySource[] { (ConfigurationPropertySource)mapConfigurationPropertySource });
        binder.bind(ConfigurationPropertyName.EMPTY, Bindable.ofInstance(target));
      } catch (IOException ioe) {
        LOGGER.error("Bind pool properties with error.", ioe);
      } 
    } 
  }
}
